package model;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.annotation.XmlTransient;

import model.event.StatechartDimensionChangeEvent;
import model.event.StatechartLocationChangeEvent;
import model.util.Dimension;
import model.util.Point;

import util.CollectionChangeListener;
import util.CollectionChangeSupport;
import util.CollectionChangeEvent;

public class Workspace {

	public enum Property {
		DIMENSION, LOCATION, STATECHART_LOCATION, STATECHART_DIMENSION
	}

	private static final float DEFAULT_WORKSPACE_WIDTH = 400f;
	private static final float DEFAULT_WORKSPACE_HEIGHT = 400f;
	private User user;
	private Dimension size;
	private Point location;

	@XmlTransient
	private List<Statechart> statecharts;

	private List<Long> statechartIDs;
	private Map<Long, Point> statechartLocations;
	private Map<Long, Dimension> statechartDimensions;

	@XmlTransient
	private CollectionChangeSupport<Statechart> statechartCollectionChangeSupport = new CollectionChangeSupport<>(
			this);

	@XmlTransient
	private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
			this);

	@SuppressWarnings("unused")
	private Workspace() {
	}

	public Workspace(User user) {
		this.user = user;
		this.statecharts = new ArrayList<Statechart>();
		this.statechartIDs = new ArrayList<Long>();
		this.statechartLocations = new HashMap<Long, Point>();
		this.statechartDimensions = new HashMap<Long, Dimension>();
		this.size = new Dimension(DEFAULT_WORKSPACE_WIDTH,
				DEFAULT_WORKSPACE_HEIGHT);
		this.location = new Point();
	}

	public User getUser() {
		return user;
	}

	protected void setUser(User user) {
		this.user = user;
	}

	public List<Statechart> getStatecharts() {
		return statecharts;
	}

	public void setStatecharts(List<Statechart> statecharts) {
		this.statecharts = statecharts;
		this.statechartIDs.clear();
		for (Statechart statechart : statecharts) {
			statechartIDs.add(statechart.getId());
		}
	}
public Point getStatechartLocation(Long statechartId) {
		return statechartLocations.get(statechartId);
	}
	public Map<Long, Point> getStatechartLocations() {
		return statechartLocations;
	}
	
	public void setStatechartLocations(Map<Long, Point> statechartLocations) {
		this.statechartLocations = statechartLocations;
	}
	public Dimension getStatechartDimension(Long statechartId) {
		return statechartDimensions.get(statechartId);
	}
	public Map<Long, Dimension> getStatechartDimentions() {
		return statechartDimensions;
	}

	public void setStatechartDimensions(
			Map<Long, Dimension> statechartDimensions) {
		this.statechartDimensions = statechartDimensions;
	}

	public void setStatechartLocation(Long statechartId, Point location) {
		if (statechartIDs.contains(statechartId)) {
			Statechart statechart = getStatechartById(statechartId);
			if (statechart != null) {
				Point oldLocation = null;
				if (statechartLocations.containsKey(statechartId)) {
					oldLocation = statechartLocations.get(statechartId);
				}
				statechartLocations.put(statechartId, location);
				propertyChangeSupport
						.firePropertyChange(new StatechartLocationChangeEvent(
								this, Property.STATECHART_LOCATION.name(),
								oldLocation, location, statechart));
			}
		}
	}

	public void setStatechartDimension(Long statechartId, Dimension dimension) {
		if (statechartIDs.contains(statechartId)) {
			Statechart statechart = getStatechartById(statechartId);
			if (statechart != null) {
				Dimension oldDimension = null;
				if (statechartDimensions.containsKey(statechartId)) {
					oldDimension = statechartDimensions.get(statechartId);
				}
				statechartDimensions.put(statechartId, dimension);
				propertyChangeSupport
						.firePropertyChange(new StatechartDimensionChangeEvent(
								this, Property.STATECHART_DIMENSION.name(),
								oldDimension, dimension, statechart));
			}
		}
	}

	private Statechart getStatechartById(Long statechartId) {
		for (Statechart statechart : statecharts) {
			if (statechart.getId().equals(statechartId)) {
				return statechart;
			}
		}
		return null;
	}

	public void addStatechart(Statechart statechart) {
		this.statecharts.add(statechart);
		this.statechartIDs.add(statechart.getId());
		this.statechartCollectionChangeSupport.fireCollectionChanged(
				statechart, statecharts.indexOf(statechart),
				CollectionChangeEvent.Type.ADD);
	}

	public void removeStatechart(Statechart statechart) {
		if (statecharts.contains(statechart)) {
			int index = statecharts.indexOf(statechart);
			this.statecharts.remove(statechart);
			this.statechartIDs.remove(statechart.getId());
			this.statechartLocations.remove(statechart.getId());
			this.statechartDimensions.remove(statechart.getId());
			this.statechartCollectionChangeSupport.fireCollectionChanged(
					statechart, index, CollectionChangeEvent.Type.REMOVE);
		}
	}

	public void addCollectionChangeListener(
			CollectionChangeListener<Statechart> listener) {
		this.statechartCollectionChangeSupport.addCollectionListener(listener);
	}

	public void removeCollectionChangeListener(
			CollectionChangeListener<Statechart> listener) {
		this.statechartCollectionChangeSupport
				.removeCollectionListener(listener);
	}

	public List<Long> getStatechartIDs() {
		return statechartIDs;
	}

	public void setStatechartIDs(List<Long> statechartIDs) {
		this.statechartIDs = statechartIDs;
	}

	public void addPropertyChangeListener(Property property,
			PropertyChangeListener listener) {
		this.propertyChangeSupport.addPropertyChangeListener(property.name(),
				listener);
	}

	public void removePropertyChangeListener(Property property,
			PropertyChangeListener listener) {
		this.propertyChangeSupport.removePropertyChangeListener(
				property.name(), listener);
	}

	public Dimension getSize() {
		return size;
	}

	public void setSize(Dimension size) {
		Dimension oldSize = this.size;
		this.size = size;
		propertyChangeSupport.firePropertyChange(Property.DIMENSION.name(),
				oldSize, this.size);
	}

	public Point getLocation() {
		return location;
	}

	public void setLocation(Point location) {
		Point oldLocation = this.location;
		this.location = location;
		propertyChangeSupport.firePropertyChange(Property.LOCATION.name(),
				oldLocation, this.location);
	}

}
